package com.zzh.partnersys.service.impl;


import cn.hutool.extra.spring.SpringUtil;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.google.gson.Gson;
import com.zzh.partnersys.chat.message.request.InviteAddTeamRequestMessage;
import com.zzh.partnersys.chat.session.WSTeamSessionFactory;
import com.zzh.partnersys.common.exception.BusinessException;
import com.zzh.partnersys.common.exception.ResultCodeEnum;
import com.zzh.partnersys.entity.*;
import com.zzh.partnersys.entity.vo.TeamUserVO;
import com.zzh.partnersys.entity.vo.UserRoomVO;
import com.zzh.partnersys.es.service.ElasticsearchSyncService;
import com.zzh.partnersys.mapper.*;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.zzh.partnersys.oss.service.OSSService;
import com.zzh.partnersys.rocketmq.contant.MQConstant;
import com.zzh.partnersys.rocketmq.message.UpdateImageMes;
import com.zzh.partnersys.rocketmq.producer.MessageSender;
import com.zzh.partnersys.utils.TeamPasswordUtils;
import com.zzh.partnersys.entity.request.*;
import com.zzh.partnersys.service.TeamService;
import com.zzh.partnersys.service.UserService;
import io.netty.channel.Channel;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import org.apache.commons.lang3.StringUtils;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest;
import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import static com.zzh.partnersys.chat.cache.NettyCache.ONLINE_WS_MAP;
import static com.zzh.partnersys.constant.TeamConstant.*;
import static com.zzh.partnersys.constant.TeamStatus.PRIVATE_STATUS_TEAM;
import static com.zzh.partnersys.constant.TeamStatus.TOKEN_STATUS_TEAM;
import static com.zzh.partnersys.constant.UserConstant.ADMIN_ROLE;


/**
 * <p>
 * 队伍�?服务实现�?
 * </p>
 *
 * @author zzh
 * @since 2024-07-20
 */
@Service
public class TeamServiceImpl extends ServiceImpl<TeamMapper, TeamDO> implements TeamService {

    @Autowired
    private RedissonClient redissonClient;

    @Resource
    private UserTeamMapper userTeamMapper;

    @Resource
    private TeamMapper teamMapper;

    @Resource
    private UserFriendMapper userFriendMapper;

    @Resource
    private UserMapper userMapper;

    @Autowired
    private UserService userService;

    @Autowired
    private MessageSender messageSender;

    @Autowired
    private OSSService ossService;

    @Resource
    private ElasticsearchSyncService elasticsearchSyncService;

//    @Autowired
//    private TeamUIDCacheService teamUIDCacheService;//缓存队伍对应用户列表id的MAP是service�?


    /**
     * 添加队伍
     * 锁的力度就是去针对一个用户，不能创建无数个队�?
     * @param teamDO
     * @return
     */
    @Override
    @Transactional
    public Boolean addTeam(TeamDO teamDO) {
        //1.判断参数
        if (teamDO == null) {
            throw new BusinessException(ResultCodeEnum.PARAMS_ERROR);
        }
        //校验队伍(校验参数)

        //1. 队伍人数要在[1,10]
        Integer maxNum = teamDO.getMaxNum();
        if (maxNum == null || maxNum > 10 || maxNum < 1) {
            throw new BusinessException("队伍人数不满足要求", 40000);
        }

        //2. 队伍标题/名称 长度不超过20
        String name = teamDO.getName();
        if (StringUtils.isBlank(name) || name.length() > 20) {
            throw new BusinessException("队伍名称不满足要求", 40000);
        }

        //3. 描述不空的时候长度不能超过512
        String description = teamDO.getDescription();
        if (StringUtils.isNotBlank(description) && description.length() > 512) {
            throw new BusinessException("队伍描述不满足要求", 40000);
        }

        //4. 队伍状态不能为空，队伍状态为空默认公开
        // 4.1 如果是加密判断密码是否为空)
        Integer status = teamDO.getStatus();
        if (status != null && status == 2) {
            String password = teamDO.getPassword();
            if (StringUtils.isBlank(password)) {
                throw new BusinessException("加密队伍需要填写密码", 40000);
            }
        }


        //5. 队伍过期时间，必须超过当前时间
        LocalDateTime expireTime = teamDO.getExpireTime();
        if (LocalDateTime.now().isAfter(expireTime)) {
            throw new BusinessException("队伍持续时间不满足要求", 40000);
        }

        //6. 用户最多5个队伍 (防止创建多个队伍)
        RLock lock = redissonClient.getLock(USER_ADD_TEAM_NUM5_KEY);
        try {
            if (lock.tryLock(2000L, 3000L, TimeUnit.MILLISECONDS)) {
                QueryWrapper<UserTeamDO> queryWrapper = new QueryWrapper<>();
                queryWrapper.eq("userId", teamDO.getUserId());
                Long count = userTeamMapper.selectCount(queryWrapper);
                if (count >= 5L) {
                    throw new BusinessException("用户最多创建5个队伍", 40000);
                }
            }
        } catch (InterruptedException e) {
            throw new BusinessException("用户最多创建5个队伍", 40000);
        } finally {
            //释放�?
            if (lock.isHeldByCurrentThread()) {
                lock.unlock();
            }
        }


        //开始插入数据——队伍表 / 用户队伍关系表 (事务)
        boolean save = this.save(teamDO);
        if (!save) {
            throw new BusinessException(ResultCodeEnum.FAIL);//插入失败
        }
        UserTeamDO userTeamDO = new UserTeamDO();
        userTeamDO.setUserId(teamDO.getUserId());
        userTeamDO.setTeamId(teamDO.getId());
        int insert = userTeamMapper.insert(userTeamDO);
        if (insert < 1) {
            throw new BusinessException(ResultCodeEnum.FAIL);//插入失败
        }
        // ES 同步队伍数据(异步)
        try {
            elasticsearchSyncService.syncTeamToEs(teamDO);
        } catch (Exception e) {
            log.error("同步队伍到 ES 失败", e);
        }
        return true;
    }


    /**
     * 查询队伍列表(带分页带查询条件,带我们的查询队伍信息以及队伍对应的所有用户信�?
     *
     * @param teamSearchRequest 带分页带查询条件参数
     * @param request           用来查看当前用户是否有权限查询私�?管理�?的队�?
     * @return
     */
    @Override
    public List<TeamUserVO> searchTeamAndUserByPage(TeamSearchRequest teamSearchRequest, HttpServletRequest request) {
        //获取当前用户//判断是否能查询出公开、加密、私�?判断是否为管理员)
        boolean isAdmin = userService.isAdmin(request);
        teamSearchRequest.setIsAdmin(isAdmin);
        UserDO currentUserDO = userService.getCurrentUser(request);
        long userId = currentUserDO.getId();

        //(分页查询 + 查询条件) => 获取队伍列表
        List<TeamUserVO> teamUserVOList = teamMapper.searchTeamByCondition(teamSearchRequest);

        //针对查询出来的队伍列表进行查询出对应该队伍的用户列表
        //有队伍id,关联查询出用户id,然后查询出用户信�?

        //查不出数据直接返回null
        if ((teamUserVOList == null) || teamUserVOList.isEmpty()) {
            return null;
        }

        //1.查询出了队伍和用户的关系
        List<Long> idList = teamUserVOList.stream().map(TeamUserVO::getId).collect(Collectors.toList());
        QueryWrapper<UserTeamDO> userTeamQueryWrapper = new QueryWrapper<>();
        userTeamQueryWrapper.in("teamId", idList);
        List<UserTeamDO> userTeamDOList = userTeamMapper.selectList(userTeamQueryWrapper);
        //1.1队伍id对应用户列表id的Map
        Map<Long, List<Long>> teamIdToListUserId = userTeamDOList.stream()
                .collect(Collectors.groupingBy(UserTeamDO::getTeamId,
                        Collectors.mapping(UserTeamDO::getUserId, Collectors.toList())));

        //1.2去重的用户id -> 查询用户列表
        List<Long> userIdList = userTeamDOList.stream().map(UserTeamDO::getUserId).distinct().collect(Collectors.toList());
        List<UserDO> userDOList = userMapper.selectByIds(userIdList);
        Map<Long, UserDO> safetyUserMap = userDOList.stream().map(user -> userService.getSafeUser(user)).collect(Collectors.toMap(UserDO::getId, user -> user));

        //1.3组装返回�?队伍列表加上用户信息列表)
        for (TeamUserVO teamUserVO : teamUserVOList) {
            for (Long userId1 : teamIdToListUserId.get(teamUserVO.getId())) {
                UserDO userDO = safetyUserMap.get(userId1);
                teamUserVO.getUserDOList().add(userDO);
            }
        }

        //设置内容判断队伍是否加入�?
        for (TeamUserVO teamUserVO : teamUserVOList) {
            if (teamUserVO.getUserId() == userId) {
                teamUserVO.setAddTeam(true);
                continue;
            }
            List<UserDO> list = teamUserVO.getUserDOList().stream().filter(user -> user.getId() == userId).collect(Collectors.toList());
            if (list.size() != 0) {
                teamUserVO.setAddTeam(true);
            }
        }

        return teamUserVOList;
    }


    /**
     * 修改队伍信息,动态sql(旧值的话旧不修�?减少修改次数)
     *
     * @param teamUpdateRequest
     * @param request
     * @return
     */
    @Override
    public boolean updateTeamInfo(TeamUpdateRequest teamUpdateRequest, HttpServletRequest request) {
        if (teamUpdateRequest == null) {
            throw new BusinessException(ResultCodeEnum.PARAMS_ERROR);
        }
        Long teamId = teamUpdateRequest.getId();
        if (teamId == null || teamId <= 0) {
            throw new BusinessException(ResultCodeEnum.PARAMS_ERROR);
        }

        //1.队伍是否存在
        TeamDO teamDO = teamMapper.selectById(teamId);
        if (teamDO == null) {
            throw new BusinessException("队伍不存在", 40000);
        }

        //2.是否有权�?
        UserDO currentUserDO = userService.getCurrentUser(request);
        if (currentUserDO == null) {
            throw new BusinessException(ResultCodeEnum.NO_AUTH);
        }
        if (currentUserDO.getId() != teamDO.getUserId() && currentUserDO.getUserRole() != ADMIN_ROLE) {
            throw new BusinessException(ResultCodeEnum.NO_AUTH);
        }

        //3.先判断是否有修改的队伍人�?
        Integer maxNum = teamUpdateRequest.getMaxNum();
        if (maxNum != null && maxNum != 0 && maxNum < teamDO.getCurrentTeamNum()) {
            throw new BusinessException("队伍人数太少", 40000);
        }

        //4.过期时间是否被修改为比当前时间还前的�?
        LocalDateTime expireTime = teamUpdateRequest.getExpireTime();
        if (expireTime != null && LocalDateTime.now().isAfter(expireTime)) {
            throw new BusinessException("队伍持续时间不符合要求", 40000);
        }

        //5.如果密码有的话就进行MD5加密
        if (teamUpdateRequest.getStatus() == TOKEN_STATUS_TEAM) {
            String password = teamUpdateRequest.getPassword();
            if (StringUtils.isBlank(password)) {
                throw new BusinessException("加密队伍密码不能不填", 40000);
            }
            //MD5加密
            try {
                teamUpdateRequest.setPassword(TeamPasswordUtils.encrypt(password));
            } catch (Exception e) {
                throw new BusinessException("修改失败",4006);
            }
        }

        //调用动态修改sql
        boolean isUpdate = teamMapper.updateTeamInfo(teamDO, teamUpdateRequest);
        if (isUpdate) {
            // ES 同步队伍数据
            try {
                // 重新查询最新数据
                TeamDO updatedTeam = teamMapper.selectById(teamId);
                if (updatedTeam != null) {
                    elasticsearchSyncService.syncTeamToEs(updatedTeam);
                }
            } catch (Exception e) {
                log.error("同步队伍到 ES 失败，teamId: {}", e);
            }
        }
        return isUpdate;
    }

    /**
     * 用户加入队伍
     * 被邀请加入的�?
     * @param teamId
     * @param currentUserDO
     * @return
     */
    @Override
    @Transactional
    public boolean userJoinTeamInvite(Long teamId, UserDO currentUserDO) {
        //1.判断参数

        //2.队伍必须存在
        TeamDO teamDO = teamMapper.selectById(teamId);
        if (teamDO == null) {
            throw new BusinessException("队伍不存在", 40000);
        }

        //3.不能加入自己创建的队�?
        long userId = currentUserDO.getId();
        if (teamDO.getUserId() == userId) {
            throw new BusinessException("你是队伍的队长不需要加入", 40000);
        }

        //5.队伍是否过期
        LocalDateTime expireTime = teamDO.getExpireTime();
        if (LocalDateTime.now().isAfter(expireTime)) {
            throw new BusinessException("队伍已过期", 40000);
        }

        // ============ 加锁 ============防止多次加入队伍
        boolean isAddSuccess = false;
        //一个用户只能一个锁,一个队伍也只能一个锁
        // user1-A user1-A => 只有一个user1进去实现队伍加入A
        // user1-A user2-A => 只有一个user2/user1进去实现队伍加入
        // user1-A user1-B => 只有一个user1进去实现队伍加入A/B
        // user1-A user2-B => 互不影响
        RLock lockUserId = redissonClient.getLock(USER_ADD_TEAM_IS_ADD_USER_ID_KEY + userId);
        RLock lockTeamId = redissonClient.getLock(USER_ADD_TEAM_IS_ADD_TEAM_ID_KEY + teamId);
        try {
            if (lockUserId.tryLock(2000L, 3000L, TimeUnit.MILLISECONDS)) {
                try {
                    if (lockTeamId.tryLock(2000L, 3000L, TimeUnit.MILLISECONDS)) {
                        //6.用户 加入的队伍数不能超过  5 �?
                        QueryWrapper<UserTeamDO> userTeamQueryWrapper = new QueryWrapper<>();
                        userTeamQueryWrapper.eq("userId", userId);
                        Long countAddTeamNum = userTeamMapper.selectCount(userTeamQueryWrapper);
                        if (countAddTeamNum >= 5L) {
                            throw new BusinessException("超过最大加入队伍数", 40000);
                        }

                        //7.不能重复加入
                        userTeamQueryWrapper.eq("teamId", teamId);
                        Long oneTeamCount = userTeamMapper.selectCount(userTeamQueryWrapper);
                        if (oneTeamCount != null && oneTeamCount >= 1L) {
                            throw new BusinessException("已经加入过该队伍", 40000);
                        }

                        //8.加入的队伍当前人数是否已经满�?(应该是要加锁的！)
                        Integer currentTeamNum = teamDO.getCurrentTeamNum();
                        Integer maxNum = teamDO.getMaxNum();
                        if (currentTeamNum == maxNum) {
                            throw new BusinessException("队伍已满", 40000);
                        }

                        //9.操作数据库表
                        teamDO.setCurrentTeamNum(currentTeamNum + 1);
                        //todo 加入队伍的同时修改头像的imageUrl要变成我们新加入队伍之后拼接的图
                        teamMapper.updateById(teamDO);

                        //todo 这里可以改善为加入前判断之前是否加入过？修改isDelete数据�?
                        UserTeamDO userTeamDO = new UserTeamDO();
                        userTeamDO.setUserId(userId);
                        userTeamDO.setTeamId(teamId);

                        int insert = userTeamMapper.insert(userTeamDO);
                        if (insert > 0) {
                            //todo 缓存一�?
                            WSTeamSessionFactory.getSession().joinMember(teamId,userId);
                            isAddSuccess = true;

                            //优化发送mq消息实现更新头像
                            //rocketMQ发送消�?
                            if (messageSender == null){
                                messageSender = getMessageSender();
                            }
                            messageSender.sendMsg(MQConstant.UPDATE_TEAM_ICON_TOPIC,new UpdateImageMes(teamId, teamDO.getName()));

                            // ES 更新队伍 tags（用户加入队伍后，需要重组队伍的 tags）
                            try {
                                elasticsearchSyncService.updateTeamTags(teamId);
                            } catch (Exception e) {
                                // 使用 System.out.println 因为可能没有 log
                                System.err.println("更新队伍 tags 到 ES 失败，teamId: " + teamId + ", error: " + e.getMessage());
                            }

                        } else {
                            isAddSuccess = false;
                        }
                    }
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                } finally {
                    if (lockTeamId.isHeldByCurrentThread()) {
                        lockTeamId.unlock();
                    }
                }
            }
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        } finally {
            if (lockUserId.isHeldByCurrentThread()) {
                lockUserId.unlock();
            }
        }
        return isAddSuccess;
    }


    /**
     * 用户加入队伍
     *
     * @param userAddTeamRequest
     * @param id
     * @return
     */
    @Override
    @Transactional
    public boolean userJoinTeam(UserAddTeamRequest userAddTeamRequest, Long id) {
        long startTime = System.currentTimeMillis();

//        //1.判断参数
//        if (null == userAddTeamRequest) {
//            throw new BusinessException(ResultCodeEnum.PARAMS_ERROR);
//        }
        Long teamId = userAddTeamRequest.getId();
//        if (teamId == null || teamId <= 0) {
//            throw new BusinessException(ResultCodeEnum.PARAMS_ERROR);
//        }

        //2.队伍必须存在
        TeamDO teamDO = teamMapper.selectById(teamId);
        if (teamDO == null) {
            throw new BusinessException("队伍不存在", 40000);
        }

        long userId = id;
        //3.不能加入自己创建的队�?
        if (teamDO.getUserId() == userId) {
            throw new BusinessException("你是队伍的队长不需要加入", 40000);
        }

        //4.私有队伍不允许加�?| 加密的要匹配密码
        Integer status = teamDO.getStatus();
        if (status == PRIVATE_STATUS_TEAM) {
            throw new BusinessException("私有队伍不允许加入", 40000);
        }
        if (status == TOKEN_STATUS_TEAM) {
            String password = teamDO.getPassword();
            String passwordAddTeam = userAddTeamRequest.getPassword();
            if (!StringUtils.isBlank(passwordAddTeam)) {//MD5加密
                //MD5加密
                try {
                    passwordAddTeam = TeamPasswordUtils.encrypt(passwordAddTeam);
                } catch (Exception e) {
                    throw new BusinessException("修改失败",4006);
                }
            }
            if (StringUtils.isBlank(passwordAddTeam) || password.equals(passwordAddTeam)) {
                throw new BusinessException("密码错误", 40000);
            }
        }
        //5.队伍是否过期
        LocalDateTime expireTime = teamDO.getExpireTime();
        if (LocalDateTime.now().isAfter(expireTime)) {
            throw new BusinessException("队伍已过期", 40000);
        }

        // ============ 加锁 ============防止多次加入队伍
        boolean isAddSuccess = false;
        //一个用户只能一个锁,一个队伍也只能一个锁
        // user1-A user1-A => 只有一个user1进去实现队伍加入A
        // user1-A user2-A => 只有一个user2/user1进去实现队伍加入
        // user1-A user1-B => 只有一个user1进去实现队伍加入A/B
        // user1-A user2-B => 互不影响
        RLock lockUserId = redissonClient.getLock(USER_ADD_TEAM_IS_ADD_USER_ID_KEY + userId);
        RLock lockTeamId = redissonClient.getLock(USER_ADD_TEAM_IS_ADD_TEAM_ID_KEY + teamId);
        try {
            if (lockUserId.tryLock(2000L, 3000L, TimeUnit.MILLISECONDS)) {
                try {
                    if (lockTeamId.tryLock(2000L, 3000L, TimeUnit.MILLISECONDS)) {
                        //6.用户 加入的队伍数不能超过  5 �?
                        QueryWrapper<UserTeamDO> userTeamQueryWrapper = new QueryWrapper<>();
                        userTeamQueryWrapper.eq("userId", userId);
                        Long countAddTeamNum = userTeamMapper.selectCount(userTeamQueryWrapper);
                        if (countAddTeamNum >= 5L) {
                            throw new BusinessException("超过最大加入队伍数", 40000);
                        }

                        //7.不能重复加入
                        userTeamQueryWrapper.eq("teamId", teamId);
                        Long oneTeamCount = userTeamMapper.selectCount(userTeamQueryWrapper);
                        if (oneTeamCount != null && oneTeamCount >= 1L) {
                            throw new BusinessException("已经加入过该队伍", 40000);
                        }

                        //8.加入的队伍当前人数是否已经满(应该是要加锁的！)
                        Integer currentTeamNum = teamDO.getCurrentTeamNum();
                        Integer maxNum = teamDO.getMaxNum();
                        if (currentTeamNum == maxNum) {
                            throw new BusinessException("队伍已满", 40000);
                        }

                        //9.操作数据库表
                        teamDO.setCurrentTeamNum(currentTeamNum + 1);
                        teamMapper.updateById(teamDO);

                        //todo 这里可以改善为加入前判断之前是否加入过？修改isDelete数据�?
                        UserTeamDO userTeamDO = new UserTeamDO();
                        userTeamDO.setUserId(userId);
                        userTeamDO.setTeamId(teamId);

                        int insert = userTeamMapper.insert(userTeamDO);
                        if (insert > 0) {
                            //todo 缓存一�?
                            WSTeamSessionFactory.getSession().joinMember(teamId,userId);
                            isAddSuccess = true;

                            //优化发送mq消息实现更新头像
                            //rocketMQ发送消�?
                            if (messageSender == null){
                                messageSender = getMessageSender();
                            }
                            messageSender.sendMsg(MQConstant.UPDATE_TEAM_ICON_TOPIC,new UpdateImageMes(teamId, teamDO.getName()));


                        } else {
                            isAddSuccess = false;
                        }
                    }
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                } finally {
                    if (lockTeamId.isHeldByCurrentThread()) {
                        lockTeamId.unlock();
                    }
                }
            }
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        } finally {
            if (lockUserId.isHeldByCurrentThread()) {
                lockUserId.unlock();
            }
            System.out.println("userJoinTeam耗时:"+(System.currentTimeMillis()-startTime));
        }
        return isAddSuccess;
    }

    private MessageSender getMessageSender() {
        return SpringUtil.getBean(MessageSender.class);
    }


    /**
     * 退出队�?
     *
     * @param teamQuitRequest
     * @param userId
     * @return
     */

    @Transactional
    @Override
    public boolean quitTeam(TeamQuitRequest teamQuitRequest, long userId) {
        //1.校验参数
        if (teamQuitRequest == null) {
            throw new BusinessException(ResultCodeEnum.PARAMS_ERROR);
        }
        Integer teamId = teamQuitRequest.getId();
        if (teamId == null || teamId <= 0) {
            throw new BusinessException(ResultCodeEnum.PARAMS_ERROR);
        }

        //2.队伍必须存在
        TeamDO teamDO = teamMapper.selectById(teamId);
        if (teamDO == null) {
            throw new BusinessException("队伍不存在", 40000);
        }

        //=====================�?=====================
        RLock lock = redissonClient.getLock(USER_QUIT_TEAM_KEY + teamId);
        try {
            if (lock.tryLock(2000L, 3000L, TimeUnit.MILLISECONDS)) {
                //2. 根据队伍人数来进行分�?
                Integer currentTeamNum = teamDO.getCurrentTeamNum();
                QueryWrapper<UserTeamDO> userTeamQueryWrapper = new QueryWrapper<>();
                //2.1只剩下一�?
                if (currentTeamNum == 1) {
                    //2.1.1你是不是这个队伍的！
                    if (teamDO.getUserId() != userId) {
                        throw new BusinessException("你并不在队伍中", 40000);
                    }
                    //2.1.2删除队伍表队伍信�?删除关系表信�?
                    int deleteTeam = teamMapper.deleteById(teamId);
                    if (deleteTeam <= 0) {
                        throw new BusinessException(ResultCodeEnum.SYSTEM_ERROR);
                    }
                    userTeamQueryWrapper.eq("teamId", teamId);
                    userTeamQueryWrapper.eq("userId", userId);
                    int deleteUserTeam = userTeamMapper.delete(userTeamQueryWrapper);
                    if (deleteUserTeam <= 0) {
                        throw new BusinessException(ResultCodeEnum.SYSTEM_ERROR);
                    }
                    //2.2至少两人
                } else {
                    //2.2.1是队长则顺位
                    if (teamDO.getUserId() == userId) {
                        userTeamQueryWrapper.eq("teamId", teamId);
                        userTeamQueryWrapper.orderByAsc("joinTime");
                        userTeamQueryWrapper.last("limit 2");//取前两个(现队�?下任队长)
                        List<UserTeamDO> userTeamDOS = userTeamMapper.selectList(userTeamQueryWrapper);
                        UserTeamDO userTeamDO = userTeamDOS.get(1);//下任队长
                        Long nextUserId = userTeamDO.getUserId();//下任队长
                        teamDO.setUserId(nextUserId);
                        teamDO.setCurrentTeamNum(teamDO.getCurrentTeamNum() - 1);
                        //更新操作队伍信息
                        int updateTeam = teamMapper.updateById(teamDO);
                        if (updateTeam <= 0) {
                            throw new BusinessException(ResultCodeEnum.SYSTEM_ERROR);
                        }
                        //删除关系�?
                        int deleteUserTeam = userTeamMapper.deleteById(userTeamDOS.get(0));
                        if (deleteUserTeam <= 0) {
                            throw new BusinessException(ResultCodeEnum.SYSTEM_ERROR);
                        }

                    } else {//2.2.2不是队长直接退�?
                        //删除关系�?
                        userTeamQueryWrapper.eq("teamId", teamId);
                        userTeamQueryWrapper.eq("userId", userId);
                        int deleteUserTeam = userTeamMapper.delete(userTeamQueryWrapper);
                        if (deleteUserTeam <= 0) {
                            throw new BusinessException(ResultCodeEnum.SYSTEM_ERROR);
                        }
                        //更新操作队伍信息
                        teamDO.setCurrentTeamNum(teamDO.getCurrentTeamNum() - 1);
                        int updateTeam = teamMapper.updateById(teamDO);
                        if (updateTeam <= 0) {
                            throw new BusinessException(ResultCodeEnum.SYSTEM_ERROR);
                        }
                    }
                }
            }
        } catch (InterruptedException e) {
            throw new RuntimeException(e);

        } finally {
            if (lock.isHeldByCurrentThread()) {
                lock.unlock();
            }
        }
        return true;
    }

    /**
     * 获取当前用户加入的队伍信�?
     *
     * @param userId
     * @return
     */
    @Override
    public List<TeamUserVO> getMyJoinTeamInfo(long userId) {
        if (userId <= 0) {
            throw new BusinessException(ResultCodeEnum.PARAMS_ERROR);
        }
        //直接sql查询出关联加入到的队伍信�?
        List<TeamUserVO> teamUserVOList = teamMapper.selectUserJoinTeamList(userId);

        //如果没有创建的队伍的�?
        if (teamUserVOList == null || teamUserVOList.size() == 0) {//没有队伍
            return null;
        }


        //根据队伍列表的id，获取对应的每个队伍对应的所有用�?
        //1.查询出了队伍和用户的关系
        List<Long> idList = teamUserVOList.stream().map(TeamUserVO::getId).collect(Collectors.toList());
        QueryWrapper<UserTeamDO> userTeamQueryWrapper = new QueryWrapper<>();
        if (idList == null || idList.size() == 0) {//没有加入队伍//判断是为了保证sql语句不能�?
            return null;
        }

        userTeamQueryWrapper.in("teamId", idList);
        List<UserTeamDO> userTeamDOList = userTeamMapper.selectList(userTeamQueryWrapper);
        //1.1队伍id对应用户列表id的Map
        Map<Long, Set<Long>> teamIdToListUserId = userTeamDOList.stream()
                .collect(Collectors.groupingBy(UserTeamDO::getTeamId,
                        Collectors.mapping(UserTeamDO::getUserId, Collectors.toSet())));

        //1.2去重的用户id -> 查询用户列表
        List<Long> userIdList = userTeamDOList.stream().map(UserTeamDO::getUserId).distinct().collect(Collectors.toList());
        List<UserDO> userDOList = userMapper.selectByIds(userIdList);
        Map<Long, UserDO> safetyUserMap = userDOList.stream().map(user -> userService.getSafeUser(user)).collect(Collectors.toMap(UserDO::getId, user -> user));

        //1.3组装返回�?队伍列表加上用户信息列表)
        for (TeamUserVO teamUserVO : teamUserVOList) {
            for (Long userId1 : teamIdToListUserId.get(teamUserVO.getId())) {
                UserDO userDO = safetyUserMap.get(userId1);
                teamUserVO.getUserDOList().add(userDO);
            }
        }

        //去重！去掉那些我创建的队伍！//todo 还要联查我们用户队伍关联表，查询到这个用户对我们队伍的备注名�?
        teamUserVOList = teamUserVOList.stream().filter(teamUserVO -> teamUserVO.getUserId() != userId).collect(Collectors.toList());

        //异步缓存本地Map
        //teamUIDCacheService.CacheMapTeamUIDAddMap(teamIdToListUserId);

        return teamUserVOList;
    }


    /**
     * 获取当前用户创建的队伍信�?
     *
     * @param userId
     * @return
     */
    @Override
    public List<TeamUserVO> getMyCreateTeamInfo(long userId) {
        if (userId <= 0) {
            throw new BusinessException(ResultCodeEnum.PARAMS_ERROR);
        }
        //直接sql查询出关联创建到的队伍信�?自定义sql，规定返回内�?
        List<TeamUserVO> teamUserVOList = teamMapper.selectUserCreateTeamList(userId);
        //如果没有创建的队伍的�?
        if (teamUserVOList == null || teamUserVOList.size() == 0) {//没有队伍
            return null;
        }


        //根据队伍列表的id，获取对应的每个队伍对应的所有用�?
        //1.查询出了队伍和用户的关系
        List<Long> idList = teamUserVOList.stream().map(TeamUserVO::getId).collect(Collectors.toList());
        if (idList == null || idList.size() == 0) {//没有队伍 -》防止sql语句null
            return null;
        }
        QueryWrapper<UserTeamDO> userTeamQueryWrapper = new QueryWrapper<>();
        userTeamQueryWrapper.in("teamId", idList);
        List<UserTeamDO> userTeamDOList = userTeamMapper.selectList(userTeamQueryWrapper);
        //1.1队伍id对应用户列表id的Map
        Map<Long, Set<Long>> teamIdToListUserId = userTeamDOList.stream()
                .collect(Collectors.groupingBy(UserTeamDO::getTeamId,
                        Collectors.mapping(UserTeamDO::getUserId, Collectors.toSet())));

        //1.2去重的用户id -> 查询用户列表
        List<Long> userIdList = userTeamDOList.stream().map(UserTeamDO::getUserId).distinct().collect(Collectors.toList());
        List<UserDO> userDOList = userMapper.selectByIds(userIdList);
        Map<Long, UserDO> safetyUserMap = userDOList.stream().map(user -> userService.getSafeUser(user)).collect(Collectors.toMap(UserDO::getId, user -> user));

        //1.3组装返回�?队伍列表加上用户信息列表)
        for (TeamUserVO teamUserVO : teamUserVOList) {
            for (Long userId1 : teamIdToListUserId.get(teamUserVO.getId())) {
                UserDO userDO = safetyUserMap.get(userId1);
                teamUserVO.getUserDOList().add(userDO);
            }
        }

        //异步缓存本地Map
        //teamUIDCacheService.CacheMapTeamUIDAddMap(teamIdToListUserId);

        //todo 还要联查我们用户队伍关联表，查询到这个用户对我们队伍的备注名�?
        return teamUserVOList;
    }

    /**
     * 解散队伍
     *
     * @param userId
     * @param teamId
     * @return
     */
    @Override
    @Transactional
    public boolean dissolutionTeam(long userId, long teamId) {
        if (userId <= 0) {
            throw new BusinessException(ResultCodeEnum.NO_AUTH);
        }
        if (teamId <= 0) {
            throw new BusinessException(ResultCodeEnum.PARAMS_ERROR);
        }
        //这里应该加锁！不然多个线程过来，多次删除，消耗数据库链接性能
        RLock lock = redissonClient.getLock(USER_DISSOLUTION_TEAM_KEY + teamId);
        try {
            if (lock.tryLock(3000L, 2000L, TimeUnit.MILLISECONDS)) {
                //队伍不存在
                TeamDO teamDO = teamMapper.selectById(teamId);
                if (teamDO == null) {
                    throw new BusinessException("队伍不存在", 40000);
                }
                //是不是队�?
                if (teamDO.getUserId() != userId) {
                    throw new BusinessException(ResultCodeEnum.NO_AUTH);
                }
                //解散
                int teamDelete = teamMapper.deleteById(teamId);
                int teamUserDelete = userTeamMapper.delete(new QueryWrapper<UserTeamDO>().eq("teamId", teamId));
                if (teamDelete > 0 && teamUserDelete > 0) {
                    return true;
                } else {
                    return false;
                }
            }
        } catch (InterruptedException e) {
            throw new RuntimeException("删除失败");
        } finally {
            if (lock.isHeldByCurrentThread()) {
                lock.unlock();
            }
        }
        return true;
    }


    /**
     * 查询某个队伍信息
     *
     * @param teamSelectOneRequest
     * @return
     */
    @Override
    public TeamUserVO selectOneTeam(TeamSelectOneRequest teamSelectOneRequest) {
        if (teamSelectOneRequest == null) {
            throw new BusinessException(ResultCodeEnum.PARAMS_ERROR);
        }
        Integer teamId = teamSelectOneRequest.getTeamId();
        List<Integer> userIdList = teamSelectOneRequest.getUserIdList();
        if (teamId == null || teamId <= 0) {
            throw new BusinessException(ResultCodeEnum.PARAMS_ERROR);
        }
        if (userIdList == null || userIdList.size() <= 0) {
            throw new BusinessException(ResultCodeEnum.PARAMS_ERROR);
        }
        TeamUserVO teamUserVO = teamMapper.selectByIdTeamUserVo(teamId);//修改封装返回�?
        List<UserDO> userDOList = userService.listByIds(userIdList);
        List<UserDO> safetyUserListDO = userDOList.stream().map(user -> userService.getSafeUser(user)).collect(Collectors.toList());
        teamUserVO.setUserDOList(safetyUserListDO);
        return teamUserVO;
    }



    /**
     * 查询出某个用户所关联的聊天室的相关信�?
     * @param userId
     * @param currentUserDO
     * @return
     */
    @Override
    @Transactional
    public List<UserRoomVO> selectChatRoomByUserId(Long userId, UserDO currentUserDO) {
        //1.校验参数
        if (userId == null || userId <= 0){
            throw new BusinessException(ResultCodeEnum.PARAMS_ERROR);
        }

        Set<Long> userIdList = new HashSet<>();//创建一个userIdList(保证查询队伍表和好友表时查询的用户只用查一�?
        Map<Long, Set<Long>> teamIdToListUserIdMap = new HashMap<>();//队伍对应用户列表的映�?
        Map<String, Long> friendKeyToFriendIdMap = new HashMap<>();//好友房间key对应的好友id
        Map<Long, UserDO> safetyUserMap = new HashMap<>();//所有用户的信息

        //2.查询出user_team表找到关联的队伍信息(包含了加�?创建)
        List<UserRoomVO> userRoomTeamVOList = teamMapper.selectUserRoomTeamList(userId);

        //如果没有创建的队伍的�?
        if (userRoomTeamVOList != null && userRoomTeamVOList.size() != 0) {//有队�?
            //根据队伍列表的id，获取对应的每个队伍对应的所有用�?
            //2.1.查询出了队伍和用户的关系
            List<Long> teamIdList = userRoomTeamVOList.stream().map(UserRoomVO::getId).collect(Collectors.toList());
            QueryWrapper<UserTeamDO> userTeamQueryWrapper = new QueryWrapper<>();
            userTeamQueryWrapper.in("teamId", teamIdList);
            List<UserTeamDO> userTeamDOList = userTeamMapper.selectList(userTeamQueryWrapper);
            //2.2队伍id对应用户列表id的Map
            teamIdToListUserIdMap = userTeamDOList.stream()
                    .collect(Collectors.groupingBy(UserTeamDO::getTeamId,
                            Collectors.mapping(UserTeamDO::getUserId, Collectors.toSet())));

            //2.3去重的用户id -> 查询用户列表
            if (userTeamDOList != null){
                userIdList = userTeamDOList.stream().map(UserTeamDO::getUserId).distinct().collect(Collectors.toSet());
            }
        }

        //3.查询用户关联的好友表user_friend=>拿到私聊房间
        List<UserRoomVO> userRoomFriendVOList = userFriendMapper.selectUserRoomFriendList(userId);
        //判断type是否�?(邀请好友加入队�?->最新消�?
        userRoomFriendVOList.stream().forEach(
                userRoomVO -> {
                    if (userRoomVO.getType() == 4){
                        //查询最新消�?
                        userRoomVO.setContent(userRoomVO.getNotesName()!= null? userRoomVO.getNotesName():userRoomVO.getName() + " (链接)邀请加入队伍");
                    }
                }
        );

        //如果没有好友的话
        if (userRoomFriendVOList != null && userRoomFriendVOList.size() > 0) {//有好�?
            //3.1拿到好友的id Set如果存在论就不会重复添加
            Set<Long> friendIdList = userRoomFriendVOList.stream().map(UserRoomVO::getUserId).distinct().collect(Collectors.toSet());
            userIdList.addAll(friendIdList);
            //3.2拿到映射关系
            friendKeyToFriendIdMap = userRoomFriendVOList.stream()
                    .collect(Collectors.toMap(UserRoomVO::getFriendKey, UserRoomVO::getUserId));

        }

        //一次所有出现过的查询即�?!!!!!!!!!!!!!) 减少数据库的多次链接
        if (userIdList != null && userIdList.size() > 0){
            List<UserDO> userDOList = userMapper.selectByIds(userIdList);
            safetyUserMap = userDOList.stream().map(user -> userService.getSafeUser(user)).collect(Collectors.toMap(UserDO::getId, user -> user));
        }

        //2.4组装返回�?队伍列表加上用户信息列表)
        if (userRoomTeamVOList != null && userRoomTeamVOList.size() != 0) {//有队�?
            for (UserRoomVO userRoomVO : userRoomTeamVOList) {
                for (Long userId1 : teamIdToListUserIdMap.get(userRoomVO.getId())) {
                    UserDO userDO = safetyUserMap.get(userId1);
                    userRoomVO.getUserDOList().add(userDO);
                }
            }

            //异步缓存本地Map
            //teamUIDCacheService.CacheMapTeamUIDAddMap(teamIdToListUserIdMap);

        }

        if (userRoomFriendVOList != null && userRoomFriendVOList.size() > 0){
            //3.3组装返回值（好友+用户信息�?好友信息+自己的信�?�?
            for (UserRoomVO userRoomVO : userRoomFriendVOList) {
                Long friendId = friendKeyToFriendIdMap.get(userRoomVO.getFriendKey());
                UserDO userDO = safetyUserMap.get(friendId);
                userRoomVO.getUserDOList().add(userDO);
                userRoomVO.setImageUrl(userDO.getAvatarUrl());
                userRoomVO.getUserDOList().add(currentUserDO);
                userRoomVO.setType(1);//房间私聊
            }
            //合并
            userRoomTeamVOList.addAll(userRoomFriendVOList);
        }


        //todo 需要实现置�?字段需要有一个规定顺序的)
        return userRoomTeamVOList;//好友房间加入到里面了
    }


    /**
     * 邀请好友加入队�?
     * @param friendIds
     * @param teamId
     * @param request
     * @return
     */
    @Override
    public void invite(Long[] friendIds, Long teamId,HttpServletRequest request) {
        UserDO currentUserDO = userService.getCurrentUser(request);

        //查询队伍的信息！
        TeamDO teamDO = teamMapper.selectById(teamId);

        Gson gson = new Gson();

        //发送消息给用户
        /*
        {
            "type":8,
            "uid":1,
            "toUid":2,
            "content":{
                "teamId":1,
                "teamName":"队伍名称",
                "imageUrl":"队伍头像",
                "password":"<PASSWORD>"
            }
        }
         */
        long id = currentUserDO.getId();
        for (Long friendId : friendIds) {
            //构建消息
            JSONObject content = new JSONObject();
            //解密密码
            String password = teamDO.getPassword();
            try {
                password = TeamPasswordUtils.decrypt(password);
            } catch (Exception e) {
                throw new BusinessException("邀请失败", 40006);
            }
            content.put("teamId", teamDO.getId());
            content.put("teamName", teamDO.getName());
            content.put("imageUrl", teamDO.getImageUrl());
            content.put("password", password);

            InviteAddTeamRequestMessage inviteAddTeamRequestMessage = new InviteAddTeamRequestMessage();
            inviteAddTeamRequestMessage.setToUid(friendId);
            inviteAddTeamRequestMessage.setFromUid(id);
            inviteAddTeamRequestMessage.setContent(content.toString());
            String json = gson.toJson(inviteAddTeamRequestMessage);
            System.out.println("json" + json);

            //查询好友是否在线
            Channel channel = ONLINE_WS_MAP.get(friendId);
            if (channel != null){
                //发送消息私�?{type:8,uid:uid,content:content,toUid:toUid};
                // content => 头像+队伍名称+密码解密+队伍id的json字符串！
                System.out.println("在线！TeamServiceImpl.invite() 发送消息给好友" + json);
                channel.writeAndFlush(new TextWebSocketFrame(json));
            }

            //发送mq消息 => 保存到数据库
            messageSender.sendMsg(MQConstant.INVITE_ADD_TEAM_TOPIC,inviteAddTeamRequestMessage);
        }


    }
}
